package com.bifang.common.aspect;

import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import com.bifang.common.exception.AppException;
import com.bifang.common.util.OplogUtil;
import com.bifang.common.helper.RedisHelper;
import com.bifang.common.util.ResubmitLockUtil;
import com.bifang.common.util.ServiceRequestUtil;
import com.bifang.common.util.SessionUtil;
import com.bifang.common.vo.BaseAuditVO;
import com.bifang.common.vo.BasePageVO;
import com.bifang.common.vo.BaseVO;
import com.bifang.common.annotations.NoRepeatSubmit;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

/**
 * 服务请求拦截
 *
 * @author chenzh
 * @date 2018-08-15
 */
@Aspect
@Component
public class ServiceRequestAspect {
    private static final Logger logger = LoggerFactory.getLogger(ServiceRequestAspect.class);
    private static final Object PRESENT = new Object();
    @Autowired
    RedisHelper redisHelper;

    @Before(
            "(execution(public * com.bifang..*.integration..*.*(..))||execution(public * com.jwsoft..*.integration..*.*(..))) && @annotation(annotation)")
    public void before(JoinPoint joinPoint, NoRepeatSubmit annotation) {
        // 获取注解信息
        int delaySeconds = annotation.delaySeconds();
        Object[] pointArgs = joinPoint.getArgs();
        // 获取第一个参数
        if (pointArgs.length <= 0) {
            return;
        }
        String key = "";
        Object firstParam = pointArgs[0];

        ServletRequestAttributes attributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        HttpServletResponse response = attributes.getResponse();
        String acccessToken = ServiceRequestUtil.getAccessToken(request, response);
        String path = request.getServletPath();
        if (firstParam instanceof BaseVO
                || firstParam instanceof BasePageVO
                || firstParam instanceof BaseAuditVO) {
            // 解析参数
            String data = JSONUtil.toJsonStr(firstParam);
            key = ResubmitLockUtil.handleKey(acccessToken + path + data);
        }
        // 执行锁
        boolean lock = false;
        try {
            // 设置解锁key
            lock = ResubmitLockUtil.getInstance().lock(key, PRESENT);
            if (lock) {
                // 放行
                return;
            } else {
                // 响应重复提交异常
                throw new AppException("请勿操作太频繁");
            }
        } finally {
            // 设置解锁key和解锁时间
            ResubmitLockUtil.getInstance().unLock(lock, key, delaySeconds);
        }
    }

    @Pointcut(
            "execution(public * com.bifang..*.gateway.controller..*.*(..))||execution(public * com.jwsoft..*.gateway.controller..*.*(..))")
    private void allControllerMethod() {}

    @Around("allControllerMethod()")
    public Object doAround(ProceedingJoinPoint call) throws Throwable {
        HttpServletRequest request =
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                        .getRequest();
        HttpServletResponse response =
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                        .getResponse();
        // 获取目标对象对应的类名
        String className = call.getTarget().getClass().getName();
        // 获取目标对象上正在执行的方法名
        String methodName = call.getSignature().getName();
        String ip = ServletUtil.getClientIP(request);
        // 路由id
        String routerId = request.getHeader("routerId");
        Object[] arguments = call.getArgs();
        logger.info("开始执行:" + className + "." + methodName + "()方法...");
        String acccessToken = ServiceRequestUtil.getAccessToken(request, response);
        for (int i = 0; i < arguments.length; i++) {
            Object obj = arguments[i];
            if (obj instanceof ServletRequest
                    || obj instanceof HttpServletRequest
                    || obj instanceof HttpServletResponse
                    || obj instanceof ServletResponse
                    || obj instanceof MultipartFile) {
                // ServletRequest不能序列化，从入参里排除，否则报异常：java.lang.IllegalStateException: It is illegal
                // to call this method if the current request is not in asynchronous mode (i.e.
                // isAsyncStarted() returns false)
                // ServletResponse不能序列化 从入参里排除，否则报异常：java.lang.IllegalStateException:
                // getOutputStream() has already been called for this response
                continue;
            }
            // logger.info("请求信息"+i+":" + JSON.toJSONString(obj));
            if (obj instanceof BaseVO) {
                BaseVO baseReq = (BaseVO) obj;
                if (StringUtils.isNotBlank(acccessToken)) {
                    baseReq.setAccessToken(acccessToken);
                }

                baseReq.setOpRouterId(routerId);
                baseReq.setReqIp(ip);
                break;
            } else if (obj instanceof BasePageVO) {
                BasePageVO baseReq = (BasePageVO) obj;
                if (StringUtils.isNotBlank(acccessToken)) {
                    baseReq.setAccessToken(acccessToken);
                }
                baseReq.setOpRouterId(routerId);
                baseReq.setReqIp(ip);
                break;
            } else if (obj instanceof BaseAuditVO) {
                BaseAuditVO baseReq = (BaseAuditVO) obj;
                if (StringUtils.isNotBlank(acccessToken)) {
                    baseReq.setAccessToken(acccessToken);
                }
                baseReq.setOpRouterId(routerId);
                baseReq.setReqIp(ip);
                break;
            }
        }
        Object object = call.proceed();
        OplogUtil.removeOplogInfo();
        SessionUtil.removeSesionInfo();
        /***
         * if (object != null) {
         * if (object instanceof BaseVO || object instanceof BasePageVO|| object instanceof BaseAuditVO || object instanceof String) {
         * logger.info("返回信息:" + JSON.toJSONString(object));
         * }
         * }***/
        return object;
    }
}
